using System.Text.RegularExpressions;
using MultiAgentsClient.ChatDataStructures;
using MultiAgentsClient.Manager.DataModel;
using MultiAgentsClient.Service.SignalRService;

using MultiAgentsShared;
using static MultiAgentsClient.Manager.MessageManager;

namespace MultiAgentsClient.Manager;

public class ProjectManager : ManagerBase
{
    private readonly SignalRChatService signalRChatService;
    private readonly SignalRUserSettingsService signalRUserSettingsService;
    private readonly MessageWebviewOperator messageWebviewOperator;
    private readonly UserDataModel userDataModel;
    private readonly SystemConfigDataModel systemConfigDataModel;

    private readonly Regex antiInjectionRegex = new(@"^[0-9a-zA-Z\u4E00-\u9FA5\u3000-\u303F\uFF00-\uFFEF\\^$@._\-:+*?()[\]{} ]+$");
    #region events
    // recieve command from UI
    public record CreateProjectCommand(string PrjName, string PrjSettings);
    public record DeleteProjectCommand(int ProjectId);
    public record CreateChatRoomCommand(int PrjID, string ChatRoomName);
    public record SwitchChatRoomCommand(int ChatRoomIdIn);
    public record ArchiveChatRoomCommand(int ChatRoomId);
    public record DeleteChatRoomCommand(int ChatRoomId);
    public record RenameChatRoomCommand(int ChatRoomId, string ChatRoomName);
    public record FetchProjectSettingsCommand(int ProjectId);
    public record SaveProjectSettingsCommand(int ProjectId, string ProjectSettings);
    public record AllowToolCallChangedCommand(bool IsAllow);

    // recieve command from signalR
    public record CreateProjectResponseCommand(int ProjectId, string PrjName, string ProjectSettings);
    public record DeleteProjectResponseCommand(int ProjectId);
    public record CreateChatRoomResponseCommand(int ChatRoomId, int PrjId, string ChatRoomName);
    public record SwitchChatRoomResponseCommand(int ChatRoomId, int PrjId, List<ChatMessageData> ChatMessageData, int LastMessageId);
    public record ArchiveChatRoomResponseCommand(int ChatRoomId, int ProjectId);
    public record DeleteChatRoomResponseCommand(int ChatRoomId, int ProjectId);
    public record RenameChatRoomResponseCommand(int ProjectId, int ChatRoomId, string ChatRoomName);
    public record FetchProjectSettingsResponseCommand(int ProjectId, string ProjectSettings);
    public record SaveProjectSettingsResponseCommand();
    public record AllowToolCallChangedResponseCommand(bool IsAllow);

    // send command to UI
    public record CreateProjectCompleteCommand(ChatProjectDisp ChatPrjDisp);
    public record DeleteProjectCompleteCommand(int ProjectId);
    public record CreateChatRoomCompleteCommand(int ChatRoomId, ChatProjectDisp ChatPrjDisp);
    public record SwitchChatRoomCompleteCommand(List<ChatMessageDisp>? ChatMessageDisps, int ChatRoomId, string ChatRoomName);
    public record ArchiveChatRoomCompleteCommand(int ChatRoomId, ChatProjectDisp ChatPrjDisp);
    public record DeleteChatRoomCompleteCommand(int ChatRoomId, ChatProjectDisp ChatPrjDisp);
    public record RenameChatRoomCompleteCommand(int ProjectId, int ChatRoomId, string ChatRoomName);
    public record FetchProjectSettingsCompleteCommand(int ProjectId, string ProjectSettings);
    public record SaveProjectSettingsCompleteCommand();
    public record AllowToolCallChangedCompleteCommand(bool IsAllow);
    #endregion

    public ProjectManager(
        SignalRChatService signalRChatService,
        SignalRUserSettingsService signalRUserSettingsService,
        MessageWebviewOperator messageWebviewOperator,
        SystemConfigDataModel systemConfigDataModel,
        UserDataModel userDataModel)
    {
        this.signalRChatService = signalRChatService;
        this.signalRUserSettingsService = signalRUserSettingsService;
        this.messageWebviewOperator = messageWebviewOperator;
        this.userDataModel = userDataModel;
        this.systemConfigDataModel = systemConfigDataModel;

        eventBus.Subscribe<CreateProjectCommand>(cmd => { Task.Run(() => CreateProjectCommandHandler(cmd)); });
        eventBus.Subscribe<DeleteProjectCommand>(cmd => { Task.Run(() => DeleteProjectCommandHandler(cmd)); });
        eventBus.Subscribe<CreateChatRoomCommand>(cmd => { Task.Run(() => CreateChatRoomCommandHandler(cmd)); });
        eventBus.Subscribe<SwitchChatRoomCommand>(cmd => { Task.Run(() => SwitchChatRoomCommandHandler(cmd)); });
        eventBus.Subscribe<ArchiveChatRoomCommand>(cmd => { Task.Run(() => ArchiveChatRoomCommandHandler(cmd)); });
        eventBus.Subscribe<DeleteChatRoomCommand>(cmd => { Task.Run(() => DeleteChatRoomCommandHandler(cmd)); });
        eventBus.Subscribe<RenameChatRoomCommand>(cmd => { Task.Run(() => RenameChatRoomCommandHandler(cmd)); });
        eventBus.Subscribe<FetchProjectSettingsCommand>(cmd => { Task.Run(() => FetchProjectSettingsCommandHandler(cmd)); });
        eventBus.Subscribe<SaveProjectSettingsCommand>(cmd => { Task.Run(() => SaveProjectSettingsCommandHandler(cmd)); });
        eventBus.Subscribe<AllowToolCallChangedCommand>(cmd => { Task.Run(() => AllowToolCallChangedCommandHandler(cmd)); });

        eventBus.Subscribe<CreateProjectResponseCommand>(cmd => { Task.Run(() => CreateProjectResponseCommandHandler(cmd)); });
        eventBus.Subscribe<DeleteProjectResponseCommand>(cmd => { Task.Run(() => DeleteProjectResponseCommandHandler(cmd)); });
        eventBus.Subscribe<CreateChatRoomResponseCommand>(cmd => { Task.Run(() => CreateChatRoomResponseCommandHandler(cmd)); });
        eventBus.Subscribe<SwitchChatRoomResponseCommand>(cmd => { Task.Run(() => SwitchChatRoomResponseCommandHandler(cmd)); });
        eventBus.Subscribe<ArchiveChatRoomResponseCommand>(cmd => { Task.Run(() => ArchiveChatRoomResponseCommandHandler(cmd)); });
        eventBus.Subscribe<DeleteChatRoomResponseCommand>(cmd => { Task.Run(() => DeleteChatRoomResponseCommandHandler(cmd)); });
        eventBus.Subscribe<RenameChatRoomResponseCommand>(cmd => { Task.Run(() => RenameChatRoomResponseCommandHandler(cmd)); });
        eventBus.Subscribe<FetchProjectSettingsResponseCommand>(cmd => { Task.Run(() => FetchProjectSettingsResponseCommandHandler(cmd)); });
        eventBus.Subscribe<AllowToolCallChangedResponseCommand>(cmd => { Task.Run(() => AllowToolCallChangedResponseCommandHandler(cmd)); });
    }

    #region request
    private async Task CreateProjectCommandHandler(CreateProjectCommand createPrjCmd)
    {
        if (antiInjectionRegex.IsMatch(createPrjCmd.PrjName) == false)
        {
            eventBus.Publish(new SendUserMessageFailCommand());
            return;
        }

        await signalRChatService.SendSignalRRequest(SignalRChatService.ApiRequestType.CreateProjectApiRequest.ToString(), new Dictionary<string, object>()
            {
                { "sessionId", systemConfigDataModel.sessionId },
                { "userId", userDataModel.Id },
                { "projectName", createPrjCmd.PrjName },
                { "projectSettings", createPrjCmd.PrjSettings },
            });
    }
    private async Task DeleteProjectCommandHandler(DeleteProjectCommand cmd)
    {
        await signalRChatService.SendSignalRRequest(SignalRChatService.ApiRequestType.DeleteProjectApiRequest.ToString(), new Dictionary<string, object>()
            {
                { "sessionId", systemConfigDataModel.sessionId },
                { "userId", userDataModel.Id },
                { "projectId", cmd.ProjectId },
            });
    }

    private async Task CreateChatRoomCommandHandler(CreateChatRoomCommand createChatRoomCmd)
    {
        if (antiInjectionRegex.IsMatch(createChatRoomCmd.ChatRoomName) == false)
        {
            eventBus.Publish(new SendUserMessageFailCommand());
            return;
        }

        await signalRChatService.SendSignalRRequest(SignalRChatService.ApiRequestType.CreateChatRoomApiRequest.ToString(), new Dictionary<string, object>()
            {
                { "sessionId", systemConfigDataModel.sessionId },
                { "userId", userDataModel.Id },
                { "projectId", createChatRoomCmd.PrjID },
                { "chatRoomName", createChatRoomCmd.ChatRoomName },
            });
    }

    private async Task SwitchChatRoomCommandHandler(SwitchChatRoomCommand cmd)
    {
        await signalRChatService.SendSignalRRequest(SignalRChatService.ApiRequestType.SwitchChatRoomApiRequest.ToString(), new Dictionary<string, object>()
            {
                { "sessionId", systemConfigDataModel.sessionId },
                { "userId", userDataModel.Id },
                { "chatRoomId", cmd.ChatRoomIdIn },
            });
    }

    private async Task ArchiveChatRoomCommandHandler(ArchiveChatRoomCommand cmd)
    {
        await signalRUserSettingsService.SendSignalRRequest(SignalRUserSettingsService.ApiRequestType.ArchiveChatRoomApiRequest.ToString(), new Dictionary<string, object>()
            {
                { "sessionId", systemConfigDataModel.sessionId },
                { "userId", userDataModel.Id },
                { "chatRoomId", cmd.ChatRoomId },
            });
    }

    private async Task DeleteChatRoomCommandHandler(DeleteChatRoomCommand cmd)
    {
        await signalRUserSettingsService.SendSignalRRequest(SignalRUserSettingsService.ApiRequestType.DeleteChatRoomApiRequest.ToString(), new Dictionary<string, object>()
            {
                { "sessionId", systemConfigDataModel.sessionId },
                { "userId", userDataModel.Id },
                { "chatRoomId", cmd.ChatRoomId },
            });
    }

    private async Task RenameChatRoomCommandHandler(RenameChatRoomCommand cmd)
    {
        await signalRUserSettingsService.SendSignalRRequest(SignalRUserSettingsService.ApiRequestType.RenameChatRoomApiRequest.ToString(), new Dictionary<string, object>()
            {
                { "sessionId", systemConfigDataModel.sessionId },
                { "userId", userDataModel.Id },
                { "chatRoomId", cmd.ChatRoomId },
                { "chatRoomIName", cmd.ChatRoomName },
            });
    }

    private async Task FetchProjectSettingsCommandHandler(FetchProjectSettingsCommand cmd)
    {
        await signalRUserSettingsService.SendSignalRRequest(SignalRUserSettingsService.ApiRequestType.FetchProjectSettingsApiRequest.ToString(), new Dictionary<string, object>()
            {
                { "sessionId", systemConfigDataModel.sessionId },
                { "userId", userDataModel.Id },
                { "projectId", cmd.ProjectId },
            });
    }

    private async void SaveProjectSettingsCommandHandler(SaveProjectSettingsCommand cmd)
    {
        ChatProject? chatPrj = userDataModel.GetChatProject(cmd.ProjectId);
        if (chatPrj == null)
        {
            return;
        }

        chatPrj.chatProjectSettings = cmd.ProjectSettings;

        await signalRUserSettingsService.SendSignalRRequest(SignalRUserSettingsService.ApiRequestType.SaveProjectSettingsApiRequest.ToString(), new Dictionary<string, object>()
            {
                { "sessionId", systemConfigDataModel.sessionId },
                { "userId", userDataModel.Id },
                { "projectId", cmd.ProjectId },
                { "projectSettings", cmd.ProjectSettings },
            });
    }

    private async void AllowToolCallChangedCommandHandler(AllowToolCallChangedCommand cmd)
    {
        await signalRUserSettingsService.SendSignalRRequest(SignalRUserSettingsService.ApiRequestType.AllowToolCallChangedApiRequest.ToString(), new Dictionary<string, object>()
            {
                { "sessionId", systemConfigDataModel.sessionId },
                { "userId", userDataModel.Id },
                { "isAllow", cmd.IsAllow },
            });
    }
    #endregion


    #region response
    private void CreateProjectResponseCommandHandler(CreateProjectResponseCommand createPrjCmd)
    {
        ChatProject newChatPrj = userDataModel.CreateProject(createPrjCmd.ProjectId, createPrjCmd.PrjName, createPrjCmd.ProjectSettings);
        eventBus.Publish(new CreateProjectCompleteCommand(newChatPrj.ToDisp(-1)));
    }

    private void DeleteProjectResponseCommandHandler(DeleteProjectResponseCommand cmd)
    {
        userDataModel.DeleteProject(cmd.ProjectId);

        eventBus.Publish(new DeleteProjectCompleteCommand(cmd.ProjectId));
    }

    private void CreateChatRoomResponseCommandHandler(CreateChatRoomResponseCommand createChatRoomCmd)
    {
        ChatProject? targetChatPrj = userDataModel.GetChatProject(createChatRoomCmd.PrjId);
        if (targetChatPrj == null)
        {
            return;
        }

        targetChatPrj.CreateChatRoom(createChatRoomCmd.ChatRoomId, false, createChatRoomCmd.ChatRoomName);

        eventBus.Publish(
            new CreateChatRoomCompleteCommand(
                createChatRoomCmd.ChatRoomId,
                targetChatPrj.ToDisp(createChatRoomCmd.ChatRoomId))
            );

        _ = SwitchChatRoomCommandHandler(new SwitchChatRoomCommand(createChatRoomCmd.ChatRoomId));
    }

    private void SwitchChatRoomResponseCommandHandler(SwitchChatRoomResponseCommand cmd)
    {
        userDataModel.selectedChatRoomId = cmd.ChatRoomId;
        userDataModel.selectedPrjId = cmd.PrjId;

        ChatProject? chatProject = userDataModel.chatProjects.FirstOrDefault(prj => prj.Id == userDataModel.selectedPrjId);
        ChatRoom? chatRoom = chatProject?.ChatRoomList.FirstOrDefault(chatRoom => chatRoom.Id == userDataModel.selectedChatRoomId);

        if (chatRoom == null)
        {
            return;
        }

        List<ChatMessageData> chatMessageDatas = cmd.ChatMessageData;

        if (chatRoom != null && chatMessageDatas != null)
        {
            chatRoom.defaultAgent.ChatListContainer.ChatList = chatMessageDatas;
        }

        if (chatMessageDatas == null)
        {
            return;
        }

        List<ChatMessageDisp> chatMessageDatasDisps = chatMessageDatas.Select(chatMessageData => chatMessageData.ToDisp()).ToList();

        eventBus.Publish(new SwitchChatRoomCompleteCommand(chatMessageDatasDisps, cmd.ChatRoomId, chatRoom?.chatRoomName ?? throw Debug.ShouldNotHappen()));
        messageWebviewOperator.LoadMessages(chatMessageDatasDisps, cmd.LastMessageId);
        eventBus.Publish(new AllowSendMessageCommand(true));
        eventBus.Publish(new ReplyAssistantCloseCommand());
    }

    private void ArchiveChatRoomResponseCommandHandler(ArchiveChatRoomResponseCommand cmd)
    {
        ChatRoom? chatRoom = userDataModel.GetChatRoom(cmd.ChatRoomId);
        ChatProject? chatProject = userDataModel.GetChatProject(cmd.ProjectId);
        if (chatProject == null || chatRoom == null)
        {
            return;
        }

        chatProject.ChatRoomList.Remove(chatRoom);

        ChatProject? chatProjectArchived = userDataModel.chatProjectsArchived.FirstOrDefault(chatProject => chatProject.Id == cmd.ProjectId);
        if (chatProjectArchived == null)
        {
            chatProjectArchived = new(chatProject.Id, chatProject.chatProjectName, chatProject.chatProjectSettings);
            userDataModel.chatProjectsArchived.Add(chatProjectArchived);
        }

        chatProjectArchived.ChatRoomList.Add(chatRoom);

        eventBus.Publish(new ArchiveChatRoomCompleteCommand(cmd.ChatRoomId, chatProject.ToDisp(-1)));
        messageWebviewOperator.LoadMessages([], -1);
    }

    private void DeleteChatRoomResponseCommandHandler(DeleteChatRoomResponseCommand cmd)
    {
        ChatRoom? chatRoom = userDataModel.GetChatRoom(cmd.ChatRoomId);
        ChatProject? chatProject = userDataModel.GetChatProject(cmd.ProjectId);
        if (chatProject == null || chatRoom == null)
        {
            return;
        }

        chatProject.ChatRoomList.Remove(chatRoom);

        eventBus.Publish(new DeleteChatRoomCompleteCommand(cmd.ChatRoomId, chatProject.ToDisp(-1)));
        messageWebviewOperator.LoadMessages([], -1);
    }

    private void RenameChatRoomResponseCommandHandler(RenameChatRoomResponseCommand cmd)
    {
        ChatRoom? targetChatRoom = userDataModel.GetChatRoom(cmd.ChatRoomId);
        if (targetChatRoom == null)
        {
            return;
        }

        targetChatRoom.chatRoomName = cmd.ChatRoomName;

        eventBus.Publish(new RenameChatRoomCompleteCommand(cmd.ProjectId, cmd.ChatRoomId, cmd.ChatRoomName));
    }

    private void FetchProjectSettingsResponseCommandHandler(FetchProjectSettingsResponseCommand cmd)
    {
        eventBus.Publish(new FetchProjectSettingsCompleteCommand(cmd.ProjectId, cmd.ProjectSettings));
    }

    private void AllowToolCallChangedResponseCommandHandler(AllowToolCallChangedResponseCommand cmd)
    {
        eventBus.Publish(new AllowToolCallChangedCompleteCommand(cmd.IsAllow));
    }
    #endregion
}
